home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 1.iso / toolbox / src / exampleCode / opengl / xlib / pup.c < prev    next >
C/C++ Source or Header  |  1996-11-11  |  43KB  |  1,585 lines

  1. /*
  2.  * (c) Copyright 1994, Silicon Graphics, Inc.
  3.  * ALL RIGHTS RESERVED
  4.  *
  5.  * Permission to use, copy, modify, and distribute this software for
  6.  * any purpose and without fee is hereby granted, provided that the above
  7.  * copyright notice appear in all copies and that both the copyright notice
  8.  * and this permission notice appear in supporting documentation, and that
  9.  * the name of Silicon Graphics, Inc. not be used in advertising
  10.  * or publicity pertaining to distribution of the software without specific,
  11.  * written prior permission.
  12.  *
  13.  * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
  14.  * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
  15.  * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
  16.  * FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL SILICON
  17.  * GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
  18.  * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
  19.  * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
  20.  * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
  21.  * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC.  HAS BEEN
  22.  * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
  23.  * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
  24.  * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
  25.  *
  26.  * U.S. GOVERNMENT RESTRICTED RIGHTS LEGEND
  27.  * Use, duplication, or disclosure by the Government is subject to
  28.  * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
  29.  * (c)(1)(ii) of the Rights in Technical Data and Computer Software
  30.  * clause at DFARS 252.227-7013 and/or in similar or successor
  31.  * clauses in the FAR or the DOD or NASA FAR Supplement.
  32.  * Unpublished-- rights reserved under the copyright laws of the
  33.  * United States.  Contractor/manufacturer is Silicon Graphics,
  34.  * Inc., 2011 N.  Shoreline Blvd., Mountain View, CA 94039-7311.
  35.  *
  36.  * OpenGL(TM) is a trademark of Silicon Graphics, Inc.
  37.  */
  38. /*
  39.  * menu code with a sordid history.
  40.  */
  41.  
  42. #include    <stdlib.h>
  43. #include    <string.h>
  44. #include    <stdarg.h>
  45. #include    <ctype.h>
  46. #include    <assert.h>
  47. #include     <unistd.h>
  48. #include     <bstring.h>
  49. #include    <sys/time.h>
  50. #include    <sys/times.h>
  51. #include    <sys/param.h>
  52. #include     <sys/types.h>
  53.  
  54.  
  55. #include    <stdio.h>
  56.  
  57. #include    <X11/Xlib.h>    /* XXX do we need all this shit */
  58. #include    <X11/Xutil.h>
  59. #include    <X11/Xmd.h>
  60. #include    <X11/cursorfont.h>
  61.  
  62. #include    "pup.h"
  63.  
  64.  
  65. #define POST_TIMEOUT    160
  66. #define HOG_TIMEOUT    1000
  67. #define MOTION_HISTORY    4
  68.  
  69. static struct hbuf { int x, y; } history[MOTION_HISTORY];
  70. static int mhead;
  71. static int PostTimeout;
  72. static int HogTimeout;
  73. static int MenuState;
  74. static unsigned int StateTimeout;
  75.  
  76. static void set_state(int);
  77. static void post_submenu(void);
  78. static void next_event(Display*,XEvent*);
  79. static void timeout_happened(void);
  80. static int history_allows(void);
  81.  
  82. #define MS_NORMALSTATE        0
  83. #define    MS_PULLRIGHTPENDING    1
  84. #define MS_PULLRIGHTACTIVE    2
  85. #define MS_HOGSTATE        3
  86.  
  87. static char* defaultMenuFont = "-*-helvetica-bold-o-normal--14-100-*";
  88.  
  89. #define ALLOC(x)  malloc(x)
  90. #define FREE(x)   free(x)
  91. #define STRDUP(x) strdup(x)
  92.  
  93. static char m_class[] = "Menu";
  94. static char m_inst[] = "menu";
  95.  
  96. static void render_outline (Display* , Window , GC *, int , int , int , int );
  97. static void render_fill (Display* , Window , GC *, GC, int , int , int , int );
  98.  
  99. typedef struct mFont {
  100.     XFontStruct*    finfo;
  101.     char*        fontName;
  102.     Display        *dpy;
  103.     struct mFont*    next;
  104. } mFont;
  105.  
  106. #define Menu_itemTopInset 2
  107. #define Menu_itemBottomInset 2
  108. #define Menu_itemLeftInset 4
  109. #define Menu_itemRightInset 4
  110. #define Menu_itemOffset 2
  111.  
  112. #define Menu_topInset 4
  113. #define Menu_bottomInset 4
  114. #define Menu_leftInset 4
  115. #define Menu_rightInset 4
  116.  
  117. #define Menu_arrowLeftInset 7
  118. #define Menu_arrowWidth 8
  119. #define Menu_arrowHeight 8
  120. #define Menu_arrowRightInset 0
  121.  
  122. #define  Menu_checkLeftInset 4
  123. #define  Menu_checkWidth 11
  124. #define  Menu_checkHeight 11
  125. #define  Menu_checkRightInset 3
  126.  
  127. #define Menu_subMenuXOffset -2
  128. #define Menu_subMenuYOffset -3
  129.  
  130. #define Menu_shadowXOffset 5 + 2    /* +2 for border width */
  131. #define Menu_shadowYOffset 5 + 2
  132.  
  133. #define Menu_titleYGap 6
  134.  
  135. #define Menu_arrowAdjust    \
  136.     (Menu_arrowLeftInset+Menu_arrowWidth+Menu_arrowRightInset)
  137.  
  138. #define Menu_checkAdjust \
  139.     (Menu_checkLeftInset + Menu_checkWidth + Menu_checkRightInset)
  140.  
  141. #define Menu_widthAdjust (Menu_leftInset+Menu_rightInset)
  142.  
  143. #define Menu_heightAdjust (Menu_topInset+Menu_bottomInset)
  144.  
  145. /*---------------------------------------------------------------------- */
  146.  
  147. typedef struct struct_PopupInfo {
  148.     struct struct_PopupInfo    *next;
  149.     Display*        dpy;
  150.     int            screen;
  151.     int            overFirst;
  152.     GC            black,
  153.             darkGrey,
  154.             grey,
  155.             lightGrey,
  156.             lightWhiteGrey,
  157.             white;
  158.     int            whitePixel,
  159.             blackPixel,
  160.             lightGreyPixel;
  161.     Colormap        colormap;
  162.     Visual*        visual;
  163.     int            depth;
  164.     Pixmap        shadowPixmap;
  165.  
  166.     Pixmap        lightWhiteGreyPixmap;
  167.     GC            titleColors[4];
  168.     GC            regularColors[4];
  169.     GC            selectedColors[4];
  170.     GC            checkBoxOnColors[4];
  171.     GC            checkBoxOffColors[4];
  172.     Cursor        arrowCursor;
  173. } PopupInfo;
  174.  
  175. typedef struct Menu {
  176.     PopupInfo*            popupInfo;
  177.     struct MenuItem*    head;
  178.     struct MenuItem*    tail;
  179.     unsigned char    popping;
  180.     unsigned char    isTitle;
  181.     unsigned char    mapped;
  182.     unsigned char    button;
  183.     unsigned char    layoutDone;
  184.     unsigned char    overFirst;
  185.     short        width, height;
  186.     Window    contentWindow;
  187.     Window    shadowWindow;
  188.     int        initialX;
  189.     struct Menu*    menuTitle;
  190.  
  191.     struct MenuItem*    currentItem;    /* current selected item in this menu */
  192.     struct Menu*    currentSubMenu;    /* current active sub menu (if any) */
  193.     struct Menu*    stackPrev;    /* previous menu in stack from this menu */
  194.  
  195.     struct mFont* menuFont;
  196.     int        (*menuFunc)(int);
  197. } Menu;
  198. static void draw_check(PopupInfo* , Window , int , int , int , int );
  199. static PopupInfo* create_PopupInfo(Display* , int );
  200. static Menu* create_menu(Display* , int , int);
  201. static void set_title_menu(Menu* , char *);
  202. static void destroy_menu(Menu* );
  203. static int popup_menu(Menu* , int, int , int );
  204. static struct MenuItem* find_item(Menu*,int);
  205.  
  206. #define MenuItem_normal 0
  207. #define MenuItem_hasCheckBox 1
  208.  
  209. /* MenuItem::visualState */
  210. #define MenuItem_disabled 0
  211. #define MenuItem_quiet 1
  212. #define MenuItem_selected 2
  213.  
  214. /* Generic menu item type.  Subclasses provide a particular thing to */
  215. /* image as the label part of the menu item. */
  216.  
  217. typedef struct MenuItem {
  218.     struct Menu*    parent;
  219.     struct MenuItem*    nextItem;
  220.     struct Menu*    subMenu;
  221.     char    type;
  222.     char    visualState;
  223.     int        checkState;
  224.     int        enabled;
  225.     short    index;            /* item # for client */
  226.     int        pickValue;
  227.     int        inset;
  228.     short    x, y, width, height;    /* position in parent */
  229.     short    trueWidth, trueHeight;
  230.  
  231.     char*    label;            /* If it is a label, these are */
  232.     int        len;            /* used.  If label is zero, it */
  233.     struct mFont*    font;            /* is assumed to be a line */
  234.  
  235.     int useMenuFunc;
  236.     int    (*itemFunc)(int);
  237. } MenuItem;
  238. static MenuItem* create_item(struct Menu* , int , char* );
  239. static void reset_MenuItem(MenuItem*);
  240. static void resize_item(MenuItem*, int, int);
  241. static void move_item(MenuItem*, int, int);
  242. static void destroy_item(MenuItem* );
  243. static void render_background_item(MenuItem* );
  244. static void enter_item(MenuItem *);
  245. static void leave_item(MenuItem *);
  246. static void render_item(MenuItem *);
  247. static void set_enable_item(MenuItem* , int );
  248. static void set_check_item(MenuItem* , int );
  249.  
  250. /*---------------------------------------------------------------------*/
  251.  
  252. typedef struct aFuncElement {
  253.     int            (*func)();
  254.     struct aFuncElement *next;
  255. } aFuncElement;
  256.  
  257. typedef struct {
  258.     aFuncElement    *head;
  259.     aFuncElement    *tail;
  260. } aFuncChain;
  261.  
  262. static aFuncChain *
  263. create_aFuncChain(void) {
  264.     aFuncChain    *ret;
  265.     ret = ALLOC(sizeof *ret);
  266.     bzero(ret, sizeof *ret);
  267.     return ret;
  268. }
  269.  
  270. static void
  271. extend_aFuncChain(aFuncChain* this, int (*newfunc)()) {
  272.     aFuncElement    *newElement;
  273.     if (newfunc) {
  274.     newElement = ALLOC(sizeof *newElement);
  275.     newElement->next = 0;
  276.     newElement->func = newfunc;
  277.     if (this->head == 0)
  278.         this->head = this->tail = newElement;
  279.     else {
  280.         this->tail->next = newElement;
  281.         this->tail = newElement;
  282.     }
  283.     }
  284. }
  285.  
  286. static int
  287. call_aFuncChain(aFuncChain* this, int ret) {
  288.     aFuncElement    *who;
  289.     typedef  int (*retintargint)(int);
  290.  
  291.     for (who = this->head; who; who = who->next)
  292.     /* ret = ((int (*)(int))(who->func))(ret);*/
  293.     ret = ((retintargint)(who->func))(ret);
  294.     return ret;
  295. }
  296.  
  297. static void
  298. destroy_aFuncChain(aFuncChain* this) {
  299.     aFuncElement    *who, *youwho;
  300.     for (who = this->head; who; who = youwho) {
  301.     youwho = who->next;
  302.     FREE(who);
  303.     }
  304.     FREE(this);
  305. }
  306.     
  307. /*---------------------------------------------------------------------*/
  308.  
  309. mFont* allFontInfo;
  310. static mFont*
  311. create_font(Display* dpy, const char* fname) {
  312.     mFont*    this = allFontInfo;
  313.     XFontStruct    *font = 0;
  314.     
  315.     for ( ; this ; this = this->next)
  316.     if (this->dpy == dpy) {
  317.         if (fname == 0 && this->fontName == 0)
  318.         return this;
  319.         if (fname == 0 || this->fontName == 0)
  320.         continue;
  321.         if (strcmp(fname, this->fontName) == 0)
  322.         return this;
  323.     }
  324.     if (fname)
  325.     font = XLoadQueryFont(dpy, fname);
  326.     if (font == 0) {
  327.     font = XLoadQueryFont(dpy, defaultMenuFont);
  328.     if (font == 0)
  329.         font = XLoadQueryFont(dpy, "fixed");
  330.     }
  331.     this = ALLOC(sizeof *this);
  332.     this->dpy = dpy;
  333.     this->next = allFontInfo;
  334.     allFontInfo = this;
  335.     this->finfo = font;
  336.     this->fontName = fname ? STRDUP(fname) : 0;
  337.     return this;
  338. }
  339.  
  340. static int
  341. strwidth_font(mFont* this, char* str, int len) {
  342.     int direction, ascent, descent;
  343.     XCharStruct    size;
  344.  
  345.     XTextExtents(this->finfo, str, len, &direction, &ascent, &descent, &size);
  346.     return size.lbearing + size.rbearing;
  347. }
  348.  
  349. typedef int (*PFI)();
  350. static void
  351. do_addpup(Menu* pup,char *str,va_list args) {
  352.     MenuItem* it;
  353.     int lastItemNo = 0;
  354.     char *cp;
  355.  
  356.     for (it = pup->head; it; it = it->nextItem)
  357.      if (it->index)
  358.         lastItemNo = it->index;
  359.     for (cp = str;;cp++) {
  360.     /*
  361.     ** First collect all the characters of the string, setting
  362.     ** pieces of flag information as we go ...
  363.     */
  364.     PFI    entryFunc;
  365.     int    ignoreMenuFunc;
  366.     int    isTitle;
  367.     int    hasUnderline;
  368.     int    returnValue;
  369.     Menu*    subMenu;
  370.     char    collectName[512], *dp;
  371.  
  372.     entryFunc = 0;
  373.     ignoreMenuFunc = isTitle = hasUnderline = 0;
  374.     returnValue = lastItemNo+1;
  375.     subMenu = 0;
  376.     dp = collectName;
  377.  
  378.     while (*cp && *cp != '|') {
  379.         if (*cp != '%')
  380.         *dp++ = *cp++;
  381.         else {
  382.         switch (*++cp) {
  383.             case 't': isTitle = True; break;
  384.             case 'l': hasUnderline = True; break;
  385.             case 'n': ignoreMenuFunc = True; /* Fall through to next case */
  386.             case 'f': entryFunc = va_arg(args, PFI); break;
  387.             case 'F': pup->menuFunc = va_arg(args, PFI); break;
  388.             case 'm': subMenu = va_arg(args, Menu*); break;
  389.             case 'x':
  390.               returnValue = strtol(++cp, &cp, 10);
  391.               cp--;
  392.               break;
  393.             case '%': *dp++ = '%'; break;
  394.         }
  395.         cp++;
  396.         }
  397.     }
  398.  
  399.     if (dp == collectName)
  400.         break;
  401.     *dp = 0;
  402.     /*
  403.     ** Now we've collected all the info, add the item(s) to the menu
  404.     */
  405.     if (isTitle)
  406.         set_title_menu((Menu*) pup, collectName);
  407.     else {
  408.         it = create_item((Menu*) pup, ++lastItemNo, collectName);
  409.         it->pickValue = returnValue;
  410.         if (subMenu)
  411.         it->subMenu = subMenu;
  412.         if (ignoreMenuFunc)
  413.         it->useMenuFunc = 0;
  414.         it->itemFunc = entryFunc;
  415.         if (hasUnderline)
  416.         create_item((Menu*) pup, 0, 0);
  417.     }
  418.     if (*cp == 0)
  419.         break;
  420.     }
  421. }
  422.  
  423. long
  424. newpup(Display *dpy, int screen) {
  425.     return (long) create_menu(dpy, screen, False);
  426. }
  427.  
  428. void 
  429. addtopup(long pup, char *str, ...) {
  430.     va_list args;
  431.     va_start(args, str);
  432.     (void) do_addpup((Menu*) pup, str, args);
  433.     va_end(args);
  434. }
  435.  
  436. long
  437. defpup(Display *dpy, int scrn, char *str, ...) {
  438.     va_list args;
  439.     long ret = newpup(dpy, scrn);
  440.     va_start(args, str);
  441.     (void) do_addpup((Menu*) ret, str, args);
  442.     va_end(args);
  443.     return ret;
  444. }
  445.  
  446. long
  447. dopup(long menu) {
  448.     int ret;
  449.     Menu* pup = (Menu*) menu;
  450.     Window junkw;
  451.     int junki, x, y;
  452.     XQueryPointer(pup->popupInfo->dpy,
  453.     RootWindow(pup->popupInfo->dpy, pup->popupInfo->screen),
  454.     &junkw, &junkw, &x, &y, &junki, &junki, (unsigned *)&junki);
  455.     ret = popup_menu((Menu*) menu, 3, x, y);
  456.     XFlush(((Menu*)menu)->popupInfo->dpy);
  457.     return ret;
  458. }
  459.  
  460. void
  461. freepup(long menu) {
  462.     if (menu)
  463.     destroy_menu((Menu*) menu);
  464. }
  465.  
  466. void
  467. setpup(long menu, long itemNo, unsigned long arg) {
  468.     MenuItem* it;
  469.  
  470.     if (it = find_item((Menu*) menu, itemNo)) {
  471.     set_enable_item(it, ((arg & PUP_GREY) == 0));
  472.     set_check_item(it, (arg & (PUP_CHECK|PUP_BOX)));
  473.     }
  474. }
  475.  
  476. static void realize_menu(Menu* , int , int );
  477. static void map_menu(Menu* );
  478. static void enter_menu(Window , int , int );
  479. static void leave_item_menu(Menu* );
  480. static void move_to(int , int );
  481. static void enter_item_menu(Menu*,MenuItem* );
  482. static void cancel_menu(Menu* );
  483. static void render_menu(Menu* , int , int );
  484. static void append_item_menu(Menu* , MenuItem* );
  485. static void layout_menu(Menu* );
  486. static Menu* find_menu(Menu* , Window );
  487. static void resize_menu(Menu*, int, int);
  488.  
  489. static Menu* currentMenu;
  490.  
  491. static char shadowBits[16*2] = {
  492.     0xAA, 0xAA, 0x55, 0x55,
  493.     0xAA, 0xAA, 0x55, 0x55,
  494.     0xAA, 0xAA, 0x55, 0x55,
  495.     0xAA, 0xAA, 0x55, 0x55,
  496.     0xAA, 0xAA, 0x55, 0x55,
  497.     0xAA, 0xAA, 0x55, 0x55,
  498.     0xAA, 0xAA, 0x55, 0x55,
  499.     0xAA, 0xAA, 0x55, 0x55,
  500. };
  501. #define checkWidth 16
  502. #define checkHeight 8
  503. static char checkBits[] = {
  504.    0x00, 0x0f,
  505.    0x86, 0x03,
  506.    0xcf, 0x01,
  507.    0xec, 0x00,
  508.    0x78, 0x00,
  509.    0x38, 0x00,
  510.    0x10, 0x00,
  511.    0x10, 0x00,
  512. };
  513.  
  514. static char const *
  515. getresource(const char* class, const char* instance, ...) {
  516.     return 0;
  517. }
  518.  
  519. static void
  520. grab_everything(XEvent* ev, PopupInfo* pInfo) {
  521.     int press, code;
  522.     static have_active_grab;
  523.  
  524.     switch (ev->type) {
  525.       case KeyPress:
  526.     press = 1;
  527.     code = ev->xkey.keycode;
  528.     break;
  529.       case KeyRelease:
  530.     press = 0;
  531.     code = ev->xkey.keycode;
  532.     break;
  533.       case ButtonPress:
  534.     press = 1;
  535.     code = ev->xbutton.button;
  536.     break;
  537.       case ButtonRelease:
  538.     press = 0;
  539.     code = ev->xbutton.button;
  540.     break;
  541.       default:
  542.     return;
  543.     }
  544.  
  545.     if (press) {
  546.     if (!have_active_grab) {
  547.         Cursor c = pInfo->arrowCursor;
  548.         Display *dpy = pInfo->dpy;
  549.         int gp, gk;
  550.         Window window = RootWindow(dpy, pInfo->screen);
  551. #define POINTER_GRAB_MASK (PointerMotionMask|ButtonPressMask|ButtonReleaseMask)
  552.  
  553.         gp = XGrabPointer(dpy, window, True, POINTER_GRAB_MASK,
  554.                    GrabModeAsync, GrabModeAsync,
  555.                    None, c, CurrentTime);
  556.         /*
  557.         gk = XGrabKeyboard(dpy, evw->frame, True, GrabModeAsync,
  558.                     GrabModeAsync, CurrentTime);
  559.         */
  560.         if (/*(gk == GrabSuccess) &&*/ (gp == GrabSuccess)) {
  561.         /* winner */
  562.         have_active_grab = 1;
  563.         } else {
  564.         /* loser! no grab now, maybe later */
  565.         if (gp == GrabSuccess) {
  566.             XUngrabPointer(dpy, CurrentTime);
  567.         }
  568.         /*
  569.         if (gk == GrabSuccess) {
  570.             XUngrabKeyboard(dpy, CurrentTime);
  571.         }
  572.         */
  573.         }
  574.     }
  575.     } else {
  576.     Display *dpy = pInfo->dpy;
  577.     if (have_active_grab) {
  578.         XUngrabPointer(dpy, CurrentTime);
  579.         XUngrabKeyboard(dpy, CurrentTime);
  580.         have_active_grab = 0;
  581.     }
  582.     }
  583. }
  584.  
  585. static Visual *
  586. find_popup(Display *dpy, int screen, int *depth) {
  587.  
  588.     XVisualInfo *vinfo, template;
  589.     int nvisuals;
  590.  
  591.     template.screen = screen;
  592.     vinfo = XGetVisualInfo(dpy, VisualScreenMask, &template, &nvisuals);
  593.  
  594.     /* SGI convention is that first visual is PUP visual XXX */
  595.     *depth = vinfo->depth;
  596.     return vinfo->visual;
  597. }
  598.  
  599.  
  600. /*---------------------------------------------------------------------- */
  601. /* "allPopupInfo" points to a list of structures, one per screen, which  */
  602. /* contains information about how popup menus are rendered on that     */
  603. /* screen.  In the future, it may be per screen/visual pair, so that     */
  604. /* not all popups have to be in the same planes.             */
  605. /*---------------------------------------------------------------------- */
  606.  
  607. static PopupInfo* allPopupInfo;
  608.  
  609. static PopupInfo*
  610. create_PopupInfo(Display* dpy, int screen) {
  611.     PopupInfo* ret = allPopupInfo;
  612.     XGCValues    gcv;
  613.     int        isOverlay = 1;
  614.     XColor    light_def, medium_def, dark_def;
  615.     Window    root = RootWindow(dpy, screen);
  616.     const char*    cname;
  617.  
  618.     while (ret) {
  619.     if (ret->dpy == dpy && ret->screen == screen)
  620.         return ret;
  621.     ret = ret->next;
  622.     }
  623.     ret = ALLOC(sizeof(*ret));
  624.     ret->dpy = dpy;
  625.     ret->screen = screen;
  626.     ret->visual = find_popup(dpy, screen, &ret->depth);
  627.     ret->colormap = XCreateColormap(dpy, root, ret->visual, AllocNone);
  628.     ret->arrowCursor = XCreateFontCursor(dpy, XC_arrow);
  629.     ret->overFirst = False;
  630.     if (cname = getresource(m_class, m_inst, "Timeout", "hogTimeout", 0)) {
  631.     HogTimeout = atoi(cname);
  632.     FREE((char *)cname);
  633.     } else
  634.     HogTimeout = HOG_TIMEOUT;
  635.     if (cname = getresource(m_class, m_inst, "Timeout", "postTimeout", 0)) {
  636.     PostTimeout = atoi(cname);
  637.     FREE((char *)cname);
  638.     } else
  639.     PostTimeout = POST_TIMEOUT;
  640.     if (cname = getresource(m_class, m_inst, "OverFirst", "overFirst", 0)) {
  641.     if (strcasecmp(cname, "true") == 0)
  642.         ret->overFirst = True;
  643.     FREE((char *)cname);
  644.     }
  645.     
  646.     /* First color in the menu colormap is medium (default grey) */
  647.     if (cname = getresource(m_class, m_inst, "Background", "mediumColor", 0)) {
  648.     XParseColor(dpy, ret->colormap, cname, &medium_def);
  649.     FREE((char *)cname);
  650.     } else
  651.     medium_def.red = medium_def.green = medium_def.blue = 0xaa00;
  652.     XAllocColor(dpy, ret->colormap, &medium_def);
  653.  
  654.     /* 2nd color in the menu colormap is dark (default black) */
  655.     if (cname = getresource(m_class, m_inst, "Foreground", "darkColor", 0)) {
  656.     XParseColor(dpy, ret->colormap, cname, &dark_def);
  657.     FREE((char *)cname);
  658.     } else
  659.     dark_def.red = dark_def.green = dark_def.blue = 0;
  660.     XAllocColor(dpy, ret->colormap, &dark_def);
  661.  
  662.     /* 3nd color in the menu colormap is light (default white) */
  663.     if (cname = getresource(m_class, m_inst, "Background", "lightColor", 0)) {
  664.     XParseColor(dpy, ret->colormap, cname, &light_def);
  665.     FREE((char *)cname);
  666.     } else
  667.     light_def.red = light_def.green = light_def.blue = 0xff00;
  668.     XAllocColor(dpy, ret->colormap, &light_def);
  669.  
  670.     ret->shadowPixmap =
  671.         XCreatePixmapFromBitmapData(dpy, root, shadowBits, 16, 16,
  672.                     isOverlay ? 0 : light_def.pixel,
  673.                     dark_def.pixel,
  674.                     ret->depth);
  675.     
  676.     gcv.foreground = dark_def.pixel;
  677.     gcv.stipple = XCreatePixmapFromBitmapData(dpy, root, checkBits,
  678.                          checkWidth, checkHeight, 1, 0, 1);
  679.     ret->black = XCreateGC(dpy, ret->shadowPixmap, 
  680.                GCForeground|GCStipple, &gcv);
  681.     XFreePixmap(dpy, gcv.stipple);
  682.     gcv.fill_style = FillOpaqueStippled;
  683.     gcv.stipple = XCreatePixmapFromBitmapData(dpy, root,
  684.                           shadowBits, 16, 16, 0, 1, 1);
  685.     gcv.foreground = medium_def.pixel;
  686.     ret->lightGrey = XCreateGC(dpy, ret->shadowPixmap, GCForeground, &gcv);
  687.     gcv.background = dark_def.pixel;
  688.     ret->darkGrey = XCreateGC(dpy, ret->shadowPixmap,
  689.             GCForeground|GCBackground|GCFillStyle|GCStipple, &gcv);
  690.     gcv.foreground = light_def.pixel;
  691.     ret->grey = XCreateGC(dpy, ret->shadowPixmap,
  692.             GCForeground|GCBackground|GCFillStyle|GCStipple, &gcv);
  693.     ret->white = XCreateGC(dpy, ret->shadowPixmap, GCForeground, &gcv);
  694.     gcv.background = medium_def.pixel;
  695.     ret->lightWhiteGrey = XCreateGC(dpy, ret->shadowPixmap,
  696.             GCForeground|GCBackground|GCFillStyle|GCStipple, &gcv);
  697.  
  698.     ret->whitePixel = light_def.pixel;
  699.     ret->blackPixel = dark_def.pixel;
  700.     ret->lightGreyPixel = medium_def.pixel;
  701.  
  702.     XFreePixmap(dpy, gcv.stipple);
  703. #ifdef DEBUG_RENDER
  704.     {    /* Pick the four wild colors to see how things get framed! */
  705.     int i;
  706.     for (i = 0; i < 4; i++) {
  707.         gcv.foreground = i+1;
  708.         ret->regularColors[i]=
  709.         XCreateGC(dpy, ret->shadowPixmap, GCForeground, &gcv);
  710.     }
  711.     }
  712. #else
  713.     ret->regularColors[0] = ret->white;
  714.     ret->regularColors[1] = ret->darkGrey;
  715.     ret->regularColors[2] = ret->white;
  716.     ret->regularColors[3] = ret->grey;
  717. #endif
  718.     ret->titleColors[0] = ret->white;
  719.     ret->titleColors[1] = ret->darkGrey;
  720.     ret->titleColors[2] = ret->lightWhiteGrey;
  721.     ret->titleColors[3] = ret->grey;
  722.     ret->selectedColors[0] = ret->white;
  723.     ret->selectedColors[1] = ret->darkGrey;
  724.     ret->selectedColors[2] = ret->white;
  725.     ret->selectedColors[3] = ret->white;
  726.     ret->checkBoxOnColors[0] = ret->black;
  727.     ret->checkBoxOnColors[1] = ret->white;
  728.     ret->checkBoxOnColors[2] = ret->lightGrey;
  729.     ret->checkBoxOnColors[3] = ret->white;
  730.     ret->checkBoxOffColors[0] = ret->darkGrey;
  731.     ret->checkBoxOffColors[1] = ret->darkGrey;
  732.     ret->checkBoxOffColors[2] = ret->white;
  733.     ret->checkBoxOffColors[3] = ret->darkGrey;
  734.  
  735.     ret->lightWhiteGreyPixmap =
  736.         XCreatePixmapFromBitmapData(dpy, root, shadowBits, 16, 16,
  737.                          medium_def.pixel,
  738.                          light_def.pixel,
  739.                          ret->depth);
  740.  
  741.     ret->next = allPopupInfo;
  742.     allPopupInfo = ret;
  743.     return ret;
  744. }
  745.  
  746. /*---------------------------------------------------------------------- */
  747.  
  748. static Menu*
  749. create_menu(Display* dpy, int screen, int isTitle) {
  750.     Menu*            ret;
  751.     XSetWindowAttributes    init;
  752.     long            mask;
  753.  
  754.     ret = (Menu*) ALLOC(sizeof *ret);
  755.     bzero(ret, sizeof *ret);
  756.  
  757.     ret->popupInfo = create_PopupInfo(dpy, screen);
  758.     if (ret->isTitle = isTitle) {
  759.     ret->menuFont = create_font(dpy,
  760.         getresource("MenuTitle", "menuTitle", "Font", "font", 0));
  761.     init.background_pixel = ret->popupInfo->lightGreyPixel;
  762.     mask = CWBackPixel;
  763.     } else {
  764.     ret->menuFont = create_font(dpy,
  765.                 getresource(m_class, m_inst, "Font", "font", 0));
  766.     init.background_pixmap = ret->popupInfo->lightWhiteGreyPixmap;
  767.     mask = CWBackPixmap;
  768.     }
  769.     init.border_pixel = ret->popupInfo->blackPixel;
  770.     init.override_redirect = True;
  771.     init.cursor = ret->popupInfo->arrowCursor;
  772.     init.colormap = ret->popupInfo->colormap;
  773.     init.event_mask = EnterWindowMask|LeaveWindowMask|ButtonPressMask
  774.             |ButtonReleaseMask|ExposureMask|PointerMotionMask
  775.             |ButtonMotionMask;
  776.  
  777.     ret->contentWindow = XCreateWindow(dpy, RootWindow(dpy, screen),
  778.     0, 0,
  779.     100, 100,
  780.     1,
  781.     ret->popupInfo->depth,
  782.     InputOutput,
  783.     ret->popupInfo->visual,
  784.     mask|CWColormap|CWEventMask|CWBorderPixel
  785.         |CWOverrideRedirect|CWCursor,
  786.     &init
  787.     );
  788.  
  789.     init.background_pixmap = ret->popupInfo->shadowPixmap;
  790.     init.event_mask = ButtonPressMask | ButtonReleaseMask;
  791.     ret->shadowWindow = XCreateWindow(dpy, RootWindow(dpy, screen),
  792.         0, 0,
  793.         100, 100,
  794.         0, ret->popupInfo->depth,
  795.         InputOutput, ret->popupInfo->visual,
  796.         CWEventMask|CWColormap|CWBackPixmap
  797.         |CWBorderPixel|CWOverrideRedirect|CWCursor,
  798.         &init
  799.     );
  800.     return ret;
  801. }
  802.  
  803. static void
  804. set_title_menu(Menu* this, char *title) {
  805.     if (this->isTitle)
  806.     return;
  807.     if (this->menuTitle)
  808.     destroy_menu(this->menuTitle);
  809.     this->menuTitle = create_menu(this->popupInfo->dpy,
  810.                     this->popupInfo->screen, True);
  811.     create_item(this->menuTitle, 0, title);
  812.     this->layoutDone = 0;
  813. }
  814.  
  815. static void
  816. destroy_menu(Menu* this) {
  817.     if (this->head) destroy_item(this->head);
  818.     if (this->menuTitle) destroy_menu(this->menuTitle);
  819.     bzero(this, sizeof *this);
  820.     FREE(this);
  821. }
  822.  
  823. static void
  824. map_menu(Menu* this) {
  825.     Display *dpy = this->popupInfo->dpy;
  826.     if (this->popping) {
  827.     XRaiseWindow(dpy, this->shadowWindow);
  828.     XResizeWindow(dpy, this->shadowWindow, this->width, this->height);
  829.     XResizeWindow(dpy, this->contentWindow, this->width, this->height);
  830.     XRaiseWindow(dpy, this->contentWindow);
  831.     /*
  832.     XMapWindow(dpy, this->shadowWindow); XXXbly
  833.     */
  834.     XMapWindow(dpy, this->contentWindow);
  835.     XMapWindow(dpy, this->shadowWindow);
  836.     this->mapped = True;
  837.     }
  838. }
  839.  
  840. /* XXX try to center menu (title?) under cursor */
  841. /* XXX need a resource for auto-select top item */
  842. /* XXX need a resource for auto-select last item (YEAH) */
  843.  
  844. static void
  845. realize_menu(Menu* this, int rx, int ry) {
  846.     Display *dpy;
  847.     int screenWidth;
  848.     int screenHeight;
  849.     int w, h;
  850.     int left, top;
  851.  
  852.     dpy = this->popupInfo->dpy;
  853.     screenWidth = DisplayWidth(dpy, this->popupInfo->screen);
  854.     screenHeight = DisplayHeight(dpy, this->popupInfo->screen);
  855.  
  856.     if (!this->isTitle) {
  857.     layout_menu(this);    /* Title layout happens from menu layout */
  858.     if (this->overFirst && this->head) {
  859.         rx -= this->head->width/2;
  860.         ry -= this->head->height/2;
  861.     }
  862.     }
  863.     if (rx < 0)
  864.     rx = 0;
  865.     w = this->width + 2;            /* add in border width */
  866.     if (rx + this->width > screenWidth) {
  867.     rx = screenWidth - w;
  868.     }
  869.  
  870.     h = this->height + 2;            /* add in border width */
  871.     if (this->menuTitle) {
  872.     int menuTitleHeight = this->menuTitle->height + Menu_titleYGap;
  873.     if (ry < menuTitleHeight) {
  874.         ry = menuTitleHeight;
  875.     }
  876.     } else if (ry < 0)
  877.     ry = 0;
  878.     if (ry + h > screenHeight) {
  879.     ry = screenHeight - h;
  880.     }
  881.     if (this->menuTitle) {
  882.     realize_menu(this->menuTitle, rx, ry - this->menuTitle->height
  883.                     - Menu_titleYGap);
  884.     }
  885.     XMoveWindow(dpy, this->contentWindow, rx, ry);
  886.     XMoveWindow(dpy, this->shadowWindow, rx + Menu_shadowXOffset,
  887.              ry + Menu_shadowYOffset);
  888.     this->popping = True;
  889.     this->currentItem = 0;
  890.     this->currentSubMenu = 0;
  891.     this->stackPrev = 0;
  892.     map_menu(this);
  893. }
  894.  
  895. static Menu*
  896. find_menu(Menu* this, Window lookfor) {
  897.     MenuItem*    it;
  898.  
  899.     if (lookfor == this->contentWindow)
  900.     return this;
  901.     if (this->menuTitle && this->menuTitle->contentWindow == lookfor)
  902.     return this->menuTitle;
  903.     for (it = this->head; it; it = it->nextItem) {
  904.     Menu* foundBelow;
  905.     if (it->subMenu && (foundBelow = find_menu(it->subMenu, lookfor)))
  906.         return foundBelow;
  907.     }
  908.     return False;
  909. }
  910.  
  911. static int
  912. popup_menu(Menu* this, int b, int rx, int ry) {
  913.     static int alreadyUp = 0;
  914.     PopupInfo* pInfo = this->popupInfo;
  915.     Display    *dpy = pInfo->dpy;
  916.     this->button = b;
  917.  
  918.     if (alreadyUp) {
  919.     fprintf(stderr, "can't call dopup from a menu function\n");
  920.     return -1;
  921.     }
  922.     XInstallColormap(dpy, pInfo->colormap);
  923.     /*XFlush(dpy);XXXbly*/
  924.     this->overFirst = pInfo->overFirst;
  925.     realize_menu(this, rx, ry);
  926.  
  927.     {
  928.     /* should check that the button is still down ... */
  929.     XEvent ev;
  930.     ev.type = ButtonPress;
  931.     grab_everything(&ev, pInfo); /* XXX */
  932.     }
  933.  
  934.     alreadyUp = 1;
  935.     /* Spin, waiting for event that dismisses the menu */
  936.     currentMenu = this;
  937.     set_state(MS_NORMALSTATE);
  938.     for (;;) {
  939.     XEvent ev;
  940.  
  941.     next_event(dpy, &ev);
  942.     switch (ev.type) {
  943.       case KeyPress:
  944.       case KeyRelease:
  945.       case ButtonPress:
  946.         grab_everything(&ev, pInfo);
  947.         break;
  948.       case ButtonRelease:
  949.         grab_everything(&ev, pInfo);
  950.         if (ev.xbutton.button == b) {
  951.         MenuItem* result = currentMenu->currentItem;
  952.         aFuncChain    *chain;
  953.         int        pick = -1;
  954.  
  955.         chain = create_aFuncChain();
  956.         if (result) {
  957.             extend_aFuncChain(chain, result->itemFunc);
  958.             if (result->useMenuFunc) {
  959.             Menu*    amen;
  960.             for (amen = result->parent; amen != NULL; amen = amen->stackPrev)
  961.                 extend_aFuncChain(chain, amen->menuFunc);
  962.             }
  963.         }
  964.         cancel_menu(this);
  965.         if (result && result->enabled && !result->subMenu)
  966.             pick = call_aFuncChain(chain, result->pickValue);
  967.         alreadyUp = 0;
  968.         destroy_aFuncChain(chain);
  969.         return pick;
  970.         }
  971.         break;
  972.       case MotionNotify:
  973.         {
  974.         int haveXY = False;
  975.         int lx, ly;
  976.         Window ww = currentMenu->contentWindow;
  977.         do {
  978.             if (ev.xmotion.window == ww) {
  979.             history[mhead].x = lx = ev.xmotion.x;
  980.             history[mhead].y = ly = ev.xmotion.y;
  981.             mhead = (mhead + 1) % MOTION_HISTORY;
  982.             haveXY = True;
  983.             }
  984.         } while (XCheckMaskEvent(dpy, ButtonMotionMask, &ev));
  985.         if (haveXY)
  986.             move_to(lx, ly);
  987.         }
  988.         break;
  989.       case EnterNotify:
  990.       case LeaveNotify:
  991.         {
  992.         Window window;
  993.         int x, y, type;
  994.  
  995.         do {
  996.             x = ev.xcrossing.x;
  997.             y = ev.xcrossing.y;
  998.             type = ev.type;
  999.             window = ev.xcrossing.window;
  1000.         } while (XCheckMaskEvent(dpy,
  1001.                 EnterWindowMask|LeaveWindowMask, &ev));
  1002.         if (type == EnterNotify)
  1003.             enter_menu(window, x, y);
  1004.         else if (MenuState != MS_HOGSTATE)
  1005.             leave_item_menu(currentMenu);
  1006.         }
  1007.         break;
  1008.       case Expose:
  1009.         {
  1010.         Window theWindow = ev.xexpose.window;
  1011.         Menu* theMenu = find_menu(this, theWindow);
  1012.         if (theMenu) {
  1013.             int topY = theMenu->height;
  1014.             int bottomY = 0;
  1015.             do {
  1016.             if (ev.xexpose.y < topY) topY = ev.xexpose.y;
  1017.             if (ev.xexpose.y+ev.xexpose.height > bottomY)
  1018.                 bottomY = ev.xexpose.y+ev.xexpose.height;
  1019.             if (ev.xexpose.count == 0)
  1020.               render_menu(theMenu, topY, bottomY);
  1021.             } while
  1022.             (XCheckWindowEvent(dpy, theWindow, ExposureMask, &ev));
  1023.         }
  1024.         break;
  1025.         }
  1026.     }
  1027.     }
  1028. }
  1029.  
  1030. static void
  1031. enter_menu(Window evw, int x, int y) {
  1032.     Menu* m;
  1033.     if ((m = currentMenu->currentSubMenu) && (evw == m->contentWindow)) {
  1034.     /* Entered the current menus sub menu.  Make the sub menu the new */
  1035.     /* current menu. */
  1036.     set_state(MS_NORMALSTATE);
  1037.     m->stackPrev = currentMenu;
  1038.     currentMenu = m;
  1039.     assert(currentMenu->currentItem == 0);
  1040.     assert(currentMenu->currentSubMenu == 0);
  1041.     move_to(x, y);
  1042.     return;
  1043.     }
  1044.  
  1045.     if (MenuState == MS_HOGSTATE
  1046.     && evw != currentMenu->currentSubMenu->contentWindow)
  1047.     return;
  1048.     set_state(MS_NORMALSTATE);
  1049.     if ((m = currentMenu->stackPrev) && (evw == m->contentWindow)) {
  1050.     /* Entered the current menus parent.  Don't cancel the sub menu */
  1051.     /* automatically.  Instead, just revert the currentMenu. */
  1052.     leave_item_menu(currentMenu);
  1053.     currentMenu->stackPrev = 0;
  1054.     currentMenu = m;
  1055.     assert(currentMenu->currentItem != 0);
  1056.     assert(currentMenu->currentSubMenu != 0);
  1057.     move_to(x, y);
  1058.     return;
  1059.     }
  1060.  
  1061.     m = currentMenu;
  1062.     while (m) {
  1063.     if (m->contentWindow == evw) {
  1064.         while (currentMenu != m) {
  1065.         leave_item_menu(currentMenu);
  1066.         currentMenu = currentMenu->stackPrev;
  1067.         }
  1068.         move_to(x, y);
  1069.         return;
  1070.     }
  1071.     m = m->stackPrev;
  1072.     }
  1073.     leave_item_menu(currentMenu);
  1074. }
  1075.  
  1076. static void
  1077. leave_item_menu(Menu* this) {
  1078.     set_state(MS_NORMALSTATE);
  1079.     if (this->currentItem) {
  1080.     leave_item(this->currentItem);
  1081.     this->currentItem = 0;
  1082.     if (this->currentSubMenu) {
  1083.         cancel_menu(this->currentSubMenu);
  1084.         this->currentSubMenu = 0;
  1085.     }
  1086.     }
  1087. }
  1088.  
  1089. /*
  1090. ** Look at the motion history buffer and see if the delta in x is
  1091. ** greater than or equal to the delta in y
  1092. */
  1093.  
  1094. static int
  1095. history_allows(void) {
  1096.     struct hbuf *oldest, *newest;
  1097.     int dx, dy;
  1098.  
  1099.     newest = history + (mhead+MOTION_HISTORY-1)%MOTION_HISTORY;
  1100.     oldest = history + mhead;
  1101.     dx = newest->x - oldest->x;
  1102.     dy = newest->y - oldest->y;
  1103.     return dx > 0 && dy > 0 && dx*2 > dy;
  1104. }
  1105.  
  1106. static void
  1107. move_to(int evx, int evy)
  1108. {
  1109.     MenuItem* it = currentMenu->head;
  1110.     if (MenuState == MS_HOGSTATE)
  1111.     return;
  1112.     while (it) {
  1113.     /* See if evx & evyy are inside an item */
  1114.     if ((evx >= it->x) && (evx < it->x + it->width) &&
  1115.         (evy >= it->y) && (evy < it->y + it->height)) {
  1116.         if (it != currentMenu->currentItem) {
  1117.         if (MenuState == MS_PULLRIGHTACTIVE && history_allows())  {
  1118.             set_state(MS_HOGSTATE);
  1119.             return;
  1120.         }
  1121.         set_state(MS_NORMALSTATE);
  1122.         leave_item_menu(currentMenu);
  1123.         enter_item_menu(currentMenu, it);
  1124.         currentMenu->initialX = evx;
  1125.         }
  1126.         return;
  1127.     }
  1128.     it = it->nextItem;
  1129.     }
  1130.     if (MenuState == MS_PULLRIGHTACTIVE && history_allows())
  1131.     set_state(MS_HOGSTATE);
  1132.     if (MenuState != MS_HOGSTATE)
  1133.     leave_item_menu(currentMenu);
  1134. }
  1135.  
  1136. static void
  1137. enter_item_menu(Menu* this,MenuItem* it) {
  1138.     Display*    dpy = this->popupInfo->dpy;
  1139.     Menu* sub = it->subMenu;
  1140.     this->currentItem = it;
  1141.     enter_item(it);
  1142.  
  1143.     if (it->enabled && sub) {
  1144.     this->currentSubMenu = sub;
  1145.     set_state(MS_PULLRIGHTPENDING);
  1146.     }
  1147. }
  1148.  
  1149. static void
  1150. post_submenu() {
  1151.     Menu* this = currentMenu;
  1152.     MenuItem* it = currentMenu->currentItem;
  1153.     Display*    dpy = currentMenu->popupInfo->dpy;
  1154.     int rootx, rooty;
  1155.     Window child;
  1156.  
  1157.     XTranslateCoordinates(dpy, this->contentWindow,
  1158.       RootWindow(dpy, this->popupInfo->screen),
  1159.       it->x + it->width + Menu_subMenuXOffset,
  1160.       it->y + Menu_subMenuYOffset,
  1161.       &rootx, &rooty, &child);
  1162.     this->currentSubMenu->overFirst = False;
  1163.     realize_menu(this->currentSubMenu, rootx, rooty);
  1164.     XFlush(dpy);
  1165.     set_state(MS_PULLRIGHTACTIVE);
  1166. }
  1167.  
  1168. static void
  1169. cancel_menu(Menu* this) {
  1170.     Display* dpy = this->popupInfo->dpy;
  1171.  
  1172.     XUnmapWindow(dpy, this->shadowWindow);
  1173.     XUnmapWindow(dpy, this->contentWindow);
  1174.     set_state(MS_NORMALSTATE);
  1175.     this->mapped = False;
  1176.     if (this->menuTitle)
  1177.     cancel_menu(this->menuTitle);
  1178.     this->popping = False;
  1179.     leave_item_menu(this);
  1180.     this->stackPrev = 0;
  1181. }
  1182.  
  1183. static void
  1184. render_menu(Menu* this, int startY, int endY) {
  1185.     PopupInfo* pi = this->popupInfo;
  1186.     MenuItem* it = this->head;
  1187.     if (!this->mapped)
  1188.     return;
  1189.     render_outline(pi->dpy, this->contentWindow,
  1190.             this->isTitle ? pi->titleColors : pi->regularColors,
  1191.             0, 0, this->width, this->height);
  1192.     while (it) {
  1193.     if (it->y + it->height >= startY && it->y < endY)
  1194.         render_item(it);
  1195.     it = it->nextItem;
  1196.     }
  1197. }
  1198.  
  1199. static void
  1200. append_item_menu(Menu* this, MenuItem* it) {
  1201.     if (this->head)
  1202.     this->tail->nextItem = it;
  1203.     else
  1204.     this->head = it;
  1205.     this->tail = it;
  1206. }
  1207.  
  1208. static void
  1209. layout_menu(Menu* this) {
  1210.     /* Find maximum width and total height.  Position each menu-item at */
  1211.     /* its final x & y relative to the menu window. */
  1212.     short maxWidth = 0;
  1213.     int y = Menu_topInset;
  1214.     int checkAdjust = 0;
  1215.     MenuItem* it = this->head;
  1216.     int arrowAdjust = 0;
  1217.     if (this->layoutDone)
  1218.     return;
  1219.     this->layoutDone = 1;
  1220.     while (it) {
  1221.     short iw, ih;
  1222.     MenuItem* next = it->nextItem;
  1223.     reset_MenuItem(it);
  1224.     iw = it->width;
  1225.     ih = it->height;
  1226.     if (it->subMenu) {
  1227.         arrowAdjust = Menu_arrowAdjust;
  1228.         if (ih < Menu_arrowHeight) {
  1229.         ih = Menu_arrowHeight;
  1230.         resize_item(it, iw, ih);
  1231.         }
  1232.     }
  1233.     if (it->type == MenuItem_hasCheckBox)
  1234.         checkAdjust = Menu_checkAdjust;
  1235.     if (it->label && ih < Menu_checkHeight) ih = Menu_checkHeight;
  1236.     if (iw > maxWidth) maxWidth = iw;
  1237.     move_item(it, Menu_leftInset, y);
  1238.     y += ih;
  1239.     if (it->label && next)
  1240.         y += Menu_itemOffset;
  1241.     it = next;
  1242.     }
  1243.     maxWidth +=
  1244.     checkAdjust + arrowAdjust + Menu_itemLeftInset + Menu_itemRightInset;
  1245.     this->width = maxWidth + Menu_widthAdjust;
  1246.     if (this->menuTitle) {
  1247.     int tw;
  1248.     layout_menu(this->menuTitle);
  1249.     tw = this->menuTitle->width;
  1250.     if (this->width <= tw) {
  1251.         int adjust = tw - this->width;
  1252.         this->width = tw;
  1253.         maxWidth += adjust;
  1254.     } else
  1255.         resize_menu(this->menuTitle,this->width,this->menuTitle->height);
  1256.     }
  1257.     this->height = y + Menu_bottomInset;
  1258.  
  1259.     /* Now fix the widths of the menu items */
  1260.     for (it = this->head; (it) ; it = it->nextItem) {
  1261.     it->inset = checkAdjust;
  1262.     resize_item(it, maxWidth, it->height);
  1263.     }
  1264. }
  1265.  
  1266. static void
  1267. resize_menu(Menu* this, int w, int h) {
  1268.     this->width = w;
  1269.     this->height = h;
  1270. }
  1271.  
  1272. static MenuItem*
  1273. find_item(Menu* this, int itemNo) {
  1274.     MenuItem* it;
  1275.  
  1276.     for (it = this->head; it; it = it->nextItem)
  1277.     if (it->index == itemNo)
  1278.         return it;
  1279.     return 0;
  1280. }
  1281.  
  1282. /*----------------------------------------------------------------------*/
  1283.  
  1284. static void
  1285. draw_arrow (Display* dpy, Window win, GC in, GC edge, int x, int y, int w, int h) {
  1286.     XPoint p[5];
  1287.     p[0].x = p[4].x = x;
  1288.     p[0].y = p[4].y = y;
  1289.     p[1].x = x + w - 1;
  1290.     p[1].y = y + (h / 2) - 1;
  1291.     p[2].x = p[1].x;
  1292.     p[2].y = p[1].y + 1;
  1293.     p[3].x = x;
  1294.     p[3].y = y + h - 1;
  1295.     XFillPolygon(dpy, win, in, p, 4, Convex, CoordModeOrigin);
  1296.     XDrawLines(dpy, win, edge, p, 5, CoordModeOrigin);
  1297. }
  1298.  
  1299. /*----------------------------------------------------------------------*/
  1300.  
  1301. static MenuItem*
  1302. create_item(Menu* m, int ix, char* label) {
  1303.     MenuItem* this = ALLOC(sizeof *this);
  1304.     this->parent = m;
  1305.     append_item_menu(m, this);
  1306.     this->nextItem = 0;
  1307.     this->type = MenuItem_normal;
  1308.     this->enabled = True;
  1309.     this->visualState = MenuItem_quiet;
  1310.     this->checkState = 0;
  1311.     this->inset = 0;
  1312.     this->subMenu = 0;
  1313.     this->index = this->pickValue = ix;
  1314.     this->useMenuFunc = True;
  1315.     if (label) {
  1316.     this->label = STRDUP(label);
  1317.     this->len = strlen(label);
  1318.     this->trueWidth = this->width =
  1319.         strwidth_font(m->menuFont, label, this->len);
  1320.     this->trueHeight = this->height = Menu_itemTopInset
  1321.         + m->menuFont->finfo->ascent + m->menuFont->finfo->descent
  1322.         + Menu_itemBottomInset;
  1323.     } else {
  1324.     this->label = 0;
  1325.     this->trueWidth = this->width = 0;
  1326.     this->trueHeight = this->height = 3;
  1327.     this->enabled = False;
  1328.     }
  1329.     return this;
  1330. }
  1331.  
  1332. static void
  1333. destroy_item(MenuItem* this) {
  1334.     if (this->nextItem) destroy_item(this->nextItem);
  1335.     if (this->label) FREE(this->label);
  1336.     bzero(this, sizeof *this);
  1337.     FREE(this);
  1338. }
  1339.  
  1340. static void
  1341. set_enable_item(MenuItem* this, int on) {
  1342.     this->visualState = on ? MenuItem_quiet : MenuItem_disabled;
  1343.     this->enabled = on;
  1344. }
  1345.  
  1346. static void
  1347. set_check_item(MenuItem* this, int on) {
  1348.     this->type = (on ? MenuItem_hasCheckBox : MenuItem_normal);
  1349.     this->checkState = on;
  1350.     this->parent->layoutDone = False;
  1351. }
  1352.  
  1353. static void
  1354. render_background_item(MenuItem* this) {
  1355.     PopupInfo *pi = this->parent->popupInfo;
  1356.     Display* dpy = pi->dpy;
  1357.     Window win = this->parent->contentWindow;
  1358.  
  1359.     if (this->visualState == MenuItem_selected) {
  1360.     render_fill(dpy, win, pi->selectedColors, pi->white,
  1361.             this->x, this->y, this->width, this->height);
  1362.     }
  1363.     if (this->subMenu) {
  1364.     int xx = this->x + this->width
  1365.         -Menu_itemRightInset-Menu_arrowRightInset-Menu_arrowWidth;
  1366.     int yy = this->y + (this->height - Menu_arrowHeight) / 2;
  1367.     GC fg, bg;
  1368.     if (this->visualState == MenuItem_disabled)
  1369.         fg = pi->lightWhiteGrey, bg = pi->darkGrey;
  1370.     else
  1371.         fg = pi->white, bg = pi->black;
  1372.     draw_arrow(dpy, win, fg, bg, xx, yy,
  1373.              Menu_arrowWidth, Menu_arrowHeight);
  1374.     XDrawLine(dpy, win, bg, xx, yy + Menu_arrowHeight,
  1375.                xx + Menu_arrowWidth - 1, yy + Menu_arrowHeight/2+1);
  1376.     }
  1377.     if (this->type == MenuItem_hasCheckBox)
  1378.     draw_check(pi, win, this->checkState, this->x, this->y, this->height);
  1379. }
  1380.  
  1381. static void
  1382. enter_item(MenuItem *this) {
  1383.     if (this->enabled) {
  1384.     this->visualState = MenuItem_selected;
  1385.     render_item(this);
  1386.     }
  1387. }
  1388.  
  1389. static void
  1390. leave_item(MenuItem *this) {
  1391.     if (this->enabled) {
  1392.     this->visualState = MenuItem_quiet;
  1393.     XClearArea(this->parent->popupInfo->dpy, this->parent->contentWindow,
  1394.             this->x, this->y,
  1395.             this->width, this->height, False);
  1396.     if (this->parent->mapped)
  1397.         render_item(this);
  1398.     }
  1399. }
  1400.  
  1401. static void
  1402. render_item(MenuItem *this) {
  1403.     PopupInfo *pi = this->parent->popupInfo;
  1404.     Window    w = this->parent->contentWindow;
  1405.     if (this->label) {
  1406.     int xx = this->x + Menu_itemLeftInset;
  1407.     int yy = this->y + Menu_itemTopInset;
  1408.     GC gc;
  1409.  
  1410.     render_background_item(this);
  1411.     if (this->visualState == MenuItem_disabled)
  1412.         gc = pi->darkGrey;
  1413.     else
  1414.         gc = pi->black;
  1415.     XSetFont(pi->dpy, gc, this->parent->menuFont->finfo->fid);
  1416.     XDrawString(pi->dpy, w, gc,
  1417.             xx+this->inset, yy + this->parent->menuFont->finfo->ascent,
  1418.             this->label, this->len);
  1419.     } else {
  1420.     int yy = this->y;
  1421.     int xr = this->x + this->width;
  1422.     XDrawLine(pi->dpy, w, pi->darkGrey, this->x, yy, xr, yy);
  1423.     yy++;
  1424.     XDrawLine(pi->dpy, w, pi->white, this->x, yy, xr, yy);
  1425.     }
  1426. }
  1427.  
  1428. static void
  1429. resize_item(MenuItem* this, int w, int h) {
  1430.     this->width = w;
  1431.     this->height = h;
  1432. }
  1433.  
  1434. static void
  1435. reset_MenuItem(MenuItem* this) {
  1436.     this->width = this->trueWidth;
  1437.     this->height = this->trueHeight;
  1438. }
  1439.  
  1440. static void
  1441. move_item(MenuItem* this, int x, int y) {
  1442.     this->x = x;
  1443.     this->y = y;
  1444. }
  1445.  
  1446. static void
  1447. render_edges (Display* dpy, Window win, GC *colors, int x, int y, int w, int h) {
  1448.     XPoint    p[3];
  1449.  
  1450.     p[0].x = x;            /* Start in lower left */
  1451.     p[0].y = y + h - 1;
  1452.     p[1].x = x;            /* Go to top left */
  1453.     p[1].y = y;
  1454.     p[2].x = x + w - 2;        /* Finish in upper right */
  1455.     p[2].y = y;
  1456.     XDrawLines(dpy, win, colors[0], p, 3, CoordModeOrigin);
  1457.  
  1458.     p[0].x = x + 1;        /* Start in lower left */
  1459.     p[0].y = y + h - 1;
  1460.     p[1].x = x + w - 1;        /* Go to lower right */
  1461.     p[1].y = y + h - 1;
  1462.     p[2].x = x + w - 1;        /* Finish in upper right */
  1463.     p[2].y = y;
  1464.     XDrawLines(dpy, win, colors[1], p, 3, CoordModeOrigin);
  1465. }
  1466.  
  1467. static void
  1468. render_outline (Display* dpy, Window win, GC *colors, int x, int y, int w, int h) {
  1469.     render_edges(dpy, win, colors, x, y, w, h);
  1470.     render_edges(dpy, win, colors+2, x+1, y+1, w-2, h-2);
  1471. }
  1472.  
  1473. static void
  1474. render_fill(Display* dpy, Window win, GC *colors, GC fillColor,
  1475.     int x, int y, int w, int h) {
  1476.     render_outline(dpy, win, colors, x, y, w, h);
  1477.     XFillRectangle(dpy, win, fillColor, x+2, y+2, w-4, h-4);
  1478. }
  1479.  
  1480. static void
  1481. draw_check(PopupInfo* pi, Window w, int on, int x, int y, int ih) {
  1482.     GC *gcs;
  1483.  
  1484.     gcs = (on ? pi->checkBoxOnColors : pi->checkBoxOffColors);
  1485.     x += Menu_checkLeftInset;
  1486.     y += (ih - Menu_checkHeight) / 2;
  1487.     if (on)
  1488.     render_fill(pi->dpy, w, gcs, pi->lightGrey,
  1489.         x, y, Menu_checkWidth, Menu_checkHeight);
  1490.     if (on & PUP_CHECK) {
  1491.     int xo = (x + 2) % checkWidth;
  1492.     int yo = (y + 2) % checkHeight;
  1493.     XSetFillStyle(pi->dpy, pi->black, FillStippled);
  1494.     XSetTSOrigin(pi->dpy, pi->black, xo, yo);
  1495.     XFillRectangle(pi->dpy, w, pi->black,
  1496.                 x + 2, y + 2, checkWidth, checkHeight);
  1497.     XSetFillStyle(pi->dpy, pi->black, FillSolid);
  1498.     }
  1499. }
  1500.  
  1501. static void
  1502. set_state(int newState) {
  1503.     int delay;
  1504.     struct tms t;
  1505.  
  1506.     switch (MenuState = newState) {
  1507.     case MS_PULLRIGHTACTIVE:
  1508.     case MS_NORMALSTATE: StateTimeout = 0; return;
  1509.  
  1510.     case MS_HOGSTATE: delay = HogTimeout; break;
  1511.     case MS_PULLRIGHTPENDING: delay = PostTimeout; break;
  1512.     }
  1513.     delay = (delay * HZ) / 1000;
  1514.     StateTimeout = times(&t) + delay;
  1515. }
  1516.  
  1517. static void
  1518. timeout_happened(void) {
  1519.     switch (MenuState) {
  1520.     case MS_PULLRIGHTPENDING:
  1521.         post_submenu();
  1522.         break;
  1523.     case MS_HOGSTATE: {
  1524.         struct hbuf *last;
  1525.  
  1526.         cancel_menu(currentMenu->currentSubMenu);
  1527.         currentMenu->currentSubMenu = 0;
  1528.         set_state(MS_NORMALSTATE);
  1529.         last = history + (mhead+MOTION_HISTORY-1)%MOTION_HISTORY;
  1530.         move_to(last->x, last->y);
  1531.         break;
  1532.     }
  1533.     default:
  1534.         set_state(MS_NORMALSTATE);
  1535.         break;
  1536.     }
  1537. }
  1538.  
  1539. static void
  1540. next_event(Display* dpy, XEvent* pEv) {
  1541.     struct tms tms;
  1542.     if (StateTimeout) {
  1543.     unsigned int now = times(&tms);
  1544.  
  1545.     if (now > StateTimeout)
  1546.         timeout_happened();
  1547.     if (StateTimeout) {
  1548.         int    fd;
  1549.  
  1550.         if (XEventsQueued(dpy, QueuedAfterFlush) == 0) {
  1551.         fd = ConnectionNumber(dpy);
  1552.         do {
  1553.             struct timeval t;
  1554.             fd_set fds;
  1555.             unsigned int delta;
  1556.  
  1557.             FD_ZERO(&fds);
  1558.             FD_SET(fd, &fds);
  1559.             delta = StateTimeout - now;
  1560.             t.tv_sec = delta / HZ;
  1561.             t.tv_usec = (delta % HZ) * 1000000 / HZ;
  1562.             switch (select(fd+1, &fds, 0, 0, &t)) {
  1563.             case 0:        /* timeout happened */
  1564.                 timeout_happened();
  1565.                 if (StateTimeout == 0) {
  1566.                 fd = -1;
  1567.                 break;
  1568.                 }
  1569.                 /* else do it again, fall through to .. */
  1570.             case -1:    /* Error, should call select again */
  1571.                 now = times(&tms);
  1572.                 break;
  1573.             default:    /* Data is ready */
  1574.                 if (XEventsQueued(dpy, QueuedAfterReading) == 0)
  1575.                 fprintf(stderr, "botch\n"), abort();
  1576.                 fd = -1;
  1577.                 break;
  1578.             }
  1579.         } while (fd >= 0);
  1580.         }
  1581.     }
  1582.     }
  1583.     XNextEvent(dpy, pEv);
  1584. }
  1585.